package com.qiangzi.workflow.engine.service.impl;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import com.qiangzi.workflow.engine.bpmn.base.Action;
import com.qiangzi.workflow.engine.bpmn.base.Edge;
import com.qiangzi.workflow.engine.bpmn.base.Node;
import com.qiangzi.workflow.engine.bpmn.base.ProcessInstance;
import com.qiangzi.workflow.engine.bpmn.base.State;
import com.qiangzi.workflow.engine.core.NodeExecution;
import com.qiangzi.workflow.engine.service.RuntimeService;

public class MemoryRuntimeService implements RuntimeService {

    public static final Logger LOGGER = LoggerFactory.getLogger(MemoryRuntimeService.class);

    @Override
    public void run(ProcessInstance instance,
                    ApplicationContext applicationContext) throws Exception {

        run0(instance, applicationContext);

    }

    /**
     * 这里返回的节点可能是-[确实需要执行|需要被忽略]
     * 
     * @param candidates
     * @return
     * @throws Exception
     */
    private NodeExecution getNextExecution(Map<String, Node> candidates) throws Exception {

        Collection<Node> nodes = candidates.values();

        for (Node node : nodes) {

            // 当前节点必须是NEW状态
            if (State.NEW != node.getState()) {
                throw new Exception("wrong node state " + node.getId());
            }

            // 当前的点的状态是NEW-表示没有执行过,下面判断是否满足条件
            List<Edge> edges = node.getIncoming();

            // 1)如果没有边,当然可以执行
            if (null == edges || 0 == edges.size()) {
                NodeExecution execution = new NodeExecution();
                execution.setNode(node);
                execution.setAction(Action.INVOKE);
                return execution;
            }

            // 2)聚合边的状态
            int state = 0;
            for (Edge edge : edges) {
                state |= edge.getState();
            }

            // 如果存在NEW的边,肯定不会被执行
            int newState = State.NEW & state;
            if (0 != newState) {
                // 继续判断下一个节点
                continue;
            }

            // 既然所有的边都已经执行过了,那么就可以判断当前节点要执行的动作
            int invokedState = State.INVOKED & state;
            NodeExecution execution = new NodeExecution();
            execution.setNode(node);
            execution.setAction(0 != invokedState ? Action.INVOKE : Action.IGNORE);
            return execution;

        }

        return null;

    }

    private void run0(ProcessInstance instance,
                      ApplicationContext applicationContext) throws Exception {

        // 需要强调的一点是:点执行完以后顺便触发相关的边的执行,这是因为边的执行实际上会依赖于点的类型
        // 所以这个点的出边的状态交给这个点的invoke动作来触发
        NodeExecution nodeExecution;
        while (null != (nodeExecution = getNextExecution(instance.getCandidates()))) {

            // 取出
            instance.removeCandidate(nodeExecution.getNode());

            // 执行
            Node node = nodeExecution.getNode();
            Action action = nodeExecution.getAction();

            if (Action.INVOKE == action) {
                node.invoke(instance, applicationContext);
            } else {
                node.ignore(instance);
            }

            // 补充新节点
            List<Edge> edges = node.getOutgoing();
            for (Edge edge : edges) {
                Node targetNode = edge.getTargetNode();
                // 重复的会覆盖,因为内部用了hashmap,没有关系
                instance.addCandidate(targetNode);
            }

        }

        Assert.isTrue(instance.getCandidates().isEmpty(),
            "not all node executed ,check your xml config");
        LOGGER.info("instance execute done, succeed");

    }

}
